using System;
using System.Collections.Generic;
using System.Text;
using Barrage.Scenes;
using Barrage.GUI.Elements;
using System.Xml;
using Barrage.Global;
namespace Barrage.GUI
{
    /// <summary>
    /// A GUILayout is a kind of 2D Scene that holds GUIElements loaded from a XML file. Each element has some attributes in this XML file, and are loaded with a specified template so their appearance can vary.
    /// </summary>
    public class GUILayout: Scene2D
    {
        Dictionary<string,GUIElement> guiElements;

        /// <summary>
        /// Scene elements
        /// </summary>
        public Dictionary<string, GUIElement> Elements { get { return guiElements; } }
        SortedList<int, GUISelectableElement> sortedElements;
        
        string name;
        /// <summary>
        /// Scene name
        /// </summary>
        public string Name { get { return name; } }

        string template = GUITemplateTypes.Standard;
        /// <summary>
        /// Template of this layout
        /// </summary>
        public string Template { get { return template; } }

        /// <summary>
        /// Adds a selectable element to the layout
        /// </summary>
        /// <param name="e">Selectable element to be added</param>
        public void Add(GUISelectableElement e)
        {
            Add(e,true);
        }

        /// <summary>
        /// Adds a selectable element to the layout
        /// </summary>
        /// <param name="e">Selectable element to be added</param>
        /// <param name="includeAsElement">If true, the element will be added to the scene as well (so it will be rendered)</param>
        public void Add(GUISelectableElement e,bool includeAsElement)
        {
            sortedElements.Add(e.Order, e);
            Add((GUIElement)e, includeAsElement);
        }
        /// <summary>
        /// Adds an element to the layout
        /// </summary>
        /// <param name="e">Element to be added</param>
        public void Add(GUIElement e)
        {
            Add(e, true);    
        }

        /// <summary>
        /// Adds an element to the layout
        /// </summary>
        /// <param name="e">Element to be added</param>
        /// <param name="includeAsElement">If true, the element will be added to the scene as well(so it will be rendered)</param>
        public void Add(GUIElement e,bool includeAsElement)
        {
            guiElements.Add(e.Id, e);
            if (includeAsElement)
                base.Add((Element)e);
        }

        /// <summary>
        /// Constructor. Inits an empty scene
        /// </summary>
        public GUILayout()
        {
            guiElements = new Dictionary<string, GUIElement>();
            sortedElements = new SortedList<int, GUISelectableElement>();
        }

        int selectedItemIndex=0;
        /// <summary>
        /// Returns the index of the currently selected item in the layout
        /// </summary>
        public int SelectedItemIndex { get { return selectedItemIndex; } set { selectedItemIndex = value; } }

        /// <summary>
        /// Selects and returns the previous selectable item in the layout
        /// </summary>
        /// <returns>The newly selected item</returns>
        public GUISelectableElement SelectPreviousItem()
        {
            int index = sortedElements.IndexOfKey(selectedItemIndex);
            if (SelectedItem.SelectPreviousItem())
                return SelectedItem;
            if (index > 0)
                    selectedItemIndex = sortedElements.Keys[index - 1];
            return SelectedItem;
        }

        /// <summary>
        /// Selects and returns the next selectable item in the layout
        /// </summary>
        /// <returns>The newly selected item</returns>
        public GUISelectableElement SelectNextItem()
        {
            int index = sortedElements.IndexOfKey(selectedItemIndex);
            if (SelectedItem.SelectNextItem())
                return SelectedItem;
            if (index < (sortedElements.Count-1))
                selectedItemIndex = sortedElements.Keys[index + 1];
            return SelectedItem;
        }

        /// <summary>
        /// Returns the currently selected item in the layout
        /// </summary>
        public GUISelectableElement SelectedItem
        {
            get
            {
                GUISelectableElement e;
                if (sortedElements.TryGetValue(selectedItemIndex, out e))
                    return e;
                return null;
            }
        }

        int nextFreeIndex = 0;
        /// <summary>
        /// Gets the next free element index in this layout
        /// </summary>
        public int NextFreeIndex
        {
            get{
                int key = nextFreeIndex;
                bool valid=false;
                while (!valid)
                {
                    valid = !sortedElements.ContainsKey(key);
                    if (!valid)
                        key++;
                }
                nextFreeIndex = key;
                return key;
            }
        }

        //public abstract GUIButton LoadButton(int buttonId);
        private const string XML_LAYOUT_ROOT = "Layout";
        private const string XML_NAME_ATTRIBUTE = "name";
        private const string XML_TEMPLATE_ATTRIBUTE = "template";
        /// <summary>
        /// Loads a GUILayout from an XML file
        /// </summary>
        /// <param name="file">XML file name</param>
        /// <returns>Loaded GUILayout instance</returns>
        public static GUILayout Load(string file)
        {
            long initTime = Timer.DefaultTimer.CurrentTotalTime;
            GUILayout layout = new GUILayout();

            XmlDocument xd = new XmlDocument();
            xd.Load(Store.PathConfig[Store.LAYOUT_PATH] + file + ".xml");
            try
            {
                foreach (XmlNode xn in xd.ChildNodes)
                {

                    if (xn.Name.Equals(XML_LAYOUT_ROOT))
                    {
                        // If the file language is different to the current language, do nothing
                        layout.name = XmlHelper.GetXMLAttribute(xn, XML_NAME_ATTRIBUTE);
                        string template = XmlHelper.GetXMLAttribute(xn, XML_TEMPLATE_ATTRIBUTE);
                        if (template.Length > 0)
                            layout.template = template;
                        loadElements(xn.ChildNodes, layout);
                    }
                }
            }
            catch (Exception e)
            {
                throw new System.Exception("Error parsing layout file " + file + ":\n" + e.Message, e);
            }
            NLog.LogManager.GetCurrentClassLogger().Debug("Layout " + file + " created in " + (Timer.DefaultTimer.CurrentTotalTime - initTime) + " ms");
            return layout;
        }
        static void loadElements(XmlNodeList xnl, GUILayout layout)
        {
            foreach(XmlNode xn in xnl)
            {
                if (xn.NodeType != XmlNodeType.Element) continue;
                string template = XmlHelper.GetXMLAttribute(xn,XML_TEMPLATE_ATTRIBUTE);
                if (template.Length == 0)
                    template = layout.template;
                GUIElement newElement = GUIElementLoader.LoadElement(layout,xn.Name, xn,template);
                if (newElement != null)
                   layout.Add(newElement);                        
            }
        }

        /// <summary>
        /// \internal Clear resources
        /// </summary>
        public override void Dispose()
        {
            guiElements.Clear();
            guiElements = null;
            sortedElements.Clear();
            sortedElements = null;
            base.Dispose();
        }
    }
}
